﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Tzx.TUpdater.Entity;
using Tzx.TUpdater.Properties;
using Tzx.TUpdater.Utilities;

namespace Tzx.TUpdater.WinUpdater
{
    /// <summary>
    /// 更新窗体
    /// </summary>
    public partial class UpdaterFrm : BaseForm //Form
    {
        #region 构造

        /// <summary>
        /// 构造
        /// </summary>
        public UpdaterFrm()
        {
            InitializeComponent();

            //托盘图标
            notifyIcon1.Icon = Resources.AppUpdate_16;
            notifyIcon1.Text = AppGlobal.AppNameText;
            notifyIcon1.Visible = false;//不可见(暂时不用)

            //初始化
            InitializeBackgoundWorker();

            //自动下一步,不显示窗体
            isAutoNext = AppGlobal.IniAutoNext;
            isNoWindow = AppGlobal.IniMode_IsSilence;

            if (isNoWindow)
            {
                //调整:若不显示窗体,则必需自动进入下一步
                isAutoNext = true;

                //不显示窗体静默升级
                Visible = false; //窗体不可见
                WindowState = FormWindowState.Minimized; //窗体不可见前,最小化,避免闪烁
            }

            //
            Load += new EventHandler(UpdateFrm_Load);

        }

        /// <summary>
        /// 是否自动进入下一步
        /// </summary>
        private readonly bool isAutoNext = false;

        /// <summary>
        /// 不显示窗体
        /// </summary>
        private readonly bool isNoWindow = false;

        #endregion

        #region 方法

        private void ShowBalloon(string message,ToolTipIcon icon)
        {
            message = string.Format("{0}", message);
            if (message.Length == 0)
            {
                return;
            }
            bool showing = notifyIcon1.Visible;

            #region 显示
            notifyIcon1.Visible = false;
            notifyIcon1.Visible = true;
            notifyIcon1.ShowBalloonTip(
                1*1000,
                icon == ToolTipIcon.Error ? "错误" : null,
                message,
                icon
                );
            #endregion

            notifyIcon1.Visible = showing;
        }
        #endregion

        /// <summary>
        /// 1.加载时
        /// </summary>
        private void UpdateFrm_Load(object sender, EventArgs e)
        {
            AppGlobal.LogDebug("UpdateFrm_Load,Step_1前");
            string message;
            bool ok = Step_1(out message);
            AppGlobal.LogDebug("UpdateFrm_Load,后Step_1=" + ok);
            if (ok)
            {
                AppGlobal.LogDebug("UpdateFrm_Load,后Step_1=true,是否自动进入下一步=CbAutoNext.Checked=" + CbAutoNext.Checked);

                //1.是否自动进入下一步
                if (CbAutoNext.Checked)
                {
                    AppGlobal.LogDebug("自动进入下一步=true,btnNext_Click前");
                    btnNext_Click(sender, e);
                    AppGlobal.LogDebug("自动进入下一步=true,btnNext_Click后");
                }
            }
            else
            {
                AppGlobal.LogDebug("Step_1=false," + message);
            }
        }

        /// <summary>
        /// 第一步,列出待更新文件
        /// </summary>
        private bool Step_1(out string msg)
        {
            BtnNext.Enabled = true;

            //不显示窗体静默升级
            if (isNoWindow)
            {
                Visible = false; //窗体不可见
                WindowState = FormWindowState.Minimized; //窗体不可见前,最小化,避免闪烁
            }

            //
            AppGlobal.Log("开始加载更新窗体");

            CbAutoNext.Checked = isAutoNext;//true;//自动进入下一步
            CbAutoNext.Enabled = !isAutoNext;//false;//自动进入下一步

            PanelRightStep2.Visible = false;
            BtnFinish.Visible = false;

            //1.下载配置文件 - 获取服务器地址
            //string updateUrl = AppGlobal.IniUpdaterUrl;
            if (string.IsNullOrEmpty(AppGlobal.IniUpdaterUrl))
            {
                msg = "错误:没有配置更新服务器Url地址";
                AppGlobal.Log(msg);
                Close();
                return false;
            }

            AppGlobal.Log("开始下载更新配置文件");

            //1.下载配置文件 - 与服务器连接,下载更新配置文件
            string downMsg;
            //bool downloadServerConfig = appUpdater.DownloadUpdateConfigFile(AppGlobal.TempDirectoryPath, out downMsg);//被卡住
            AppGlobal.Log("临时位置:" + AppGlobal.TempDirectoryPath);
            bool downloadServerConfig = AppUpdater.GetMe().DownloadUpdateConfigFile(AppGlobal.TempDirectoryPath, out downMsg);
            if (!downloadServerConfig)
            {
                msg = "下载服务器更新配置失败:" + downMsg;
                AppGlobal.Log(msg);
                Close();
                return false;
            }

            //1.下载配置文件 - 检查配置文件
            if (!File.Exists(AppGlobal.XmlPathName_ServerFile))
            {
                msg = "下载服务器更新配置失败:没有在临时目录中找到服务端更新配置文件," + AppGlobal.TempDirectoryPath;
                AppGlobal.Log(msg);
                Close();
                return false;
            }

            //2.检查可用更新数量
            AppGlobal.Log("开始:检查可用更新数量");
            string checkMessage;
            bool checkOk = AppUpdater.GetMe().CheckForUpdate(out checkMessage);//检查可用更新数量
            if (!checkOk)
            {
                msg = "检查可用更新数量时出错:" + checkMessage;
                AppGlobal.Log(msg);
                Close();
                return false;
            }

            //3.列出可用更新
            AppGlobal.Log("开始:列出可用更新");
            int count = AppUpdater.GetMe().UpdateItems.Count;
            for (int i = 0; i < count; i++)
            {
                UpdateItem updateItem = AppUpdater.GetMe().UpdateItems[i];
                LvwUpdateNewList.Items.Add(new ListViewItem(new string[]
                {
                    updateItem.Xml_Path,
                    updateItem.Xml_Ver,
                    string.Format("{0}", updateItem.Progress)
                }));
            }

            if (count == 0 && isNoWindow)
            {
                //没有更新并且是静默安装,则关闭
                Close();
                msg = "没有更新并且是静默安装则关闭";
                return false;
            }

            //检查是否有被占用的文件
            string usedMsg;
            int uesdCount = AppUpdater.GetMe().CheckUpdateItemsFileIsUsed(out usedMsg);
            if (uesdCount > 0)
            {
                //文件被占用,不能更新,则关闭
                string log = "有" + uesdCount + "个文件被占用,不能更新:" + usedMsg;
                AppGlobal.LogDebug(log);
                AppGlobal.Log(log);

                if (!isNoWindow)
                {
                    MessageBox.Show(log, AppGlobal.AppNameText, MessageBoxButtons.OK, MessageBoxIcon.Stop);
                }

                AppGlobal.LogDebug("退出本更新程序");

                //退出,启动客户主程序EXE
                AppGlobal.Log("退出本更新程序");
                Close();
                Dispose();
                msg = "退出本更新程序," + log;
                return false;
            }

            //返回
            msg = null;
            return true;
        }

        /// <summary>
        /// 第二步,下载
        /// </summary>
        private void Step_2()
        {
            AppGlobal.LogDebug("Step_2中,开始后台下载");
            AppGlobal.Log("开始后台下载");

            const string msg = "没有可用的更新！";
            if (AppUpdater.GetMe().UpdateItems.Count > 0)
            {
                AppGlobal.LogDebug("Step_2中,AppUpdater.GetMe().UpdateItems.Count > 0");
                _bWorkerDownload.RunWorkerAsync();

                BtnNext.Enabled = false;
                BtnCancel.Enabled = true;
            }
            else
            {
                AppGlobal.LogDebug("Step_2中," + msg);
                AppGlobal.Log(msg);

                //如果不是静默升级,则弹出消息框
                if (!isNoWindow)
                {
                    MessageBox.Show(msg, AppGlobal.AppNameText, MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                return;
            }

            AppGlobal.LogDebug("Step_2中,后台下载结束");
            AppGlobal.Log("后台下载结束");
        }

        /// <summary>
        /// 第二步,不下载,取消更新
        /// </summary>
        private void Step_2_Cancel()
        {
            AppGlobal.LogDebug("Step_2_Cancel,不下载,取消更新");

            _bWorkerDownload.CancelAsync();//取消

            AppGlobal.LogDebug("Step_2_Cancel,即将退出程序");

            Close();
            Application.ExitThread();
            Application.Exit();
        }
        
        /// <summary>
        /// 第三步,替换文件,执行Action动作
        /// </summary>
        /// <returns></returns>
        private bool Step_3_bak()
        {
            //TODO:1.先复制xml配置文件,但先不替换原文件
            AppGlobal.LogDebug("Step_3,替换文件,执行Action动作");
            AppGlobal.Log("开始复制替换XML配置文件");

            //先复制xml配置文件(防止出错中断进入死循环)
            //但若复制其它文件出错,将无法真正更新,例如dll被占用
            File.Copy(
                AppGlobal.XmlPathName_ServerFile,
                AppGlobal.XmlPathName_LocalFile,
                true
                );

            AppGlobal.LogDebug("Step_3,完成复制替换XML配置文件,下面复制其它文件.");
            AppGlobal.Log("完成复制替换XML配置文件");

            //TODO:2.再复制其它文件
            bool copyOk = false;
            try
            {
                //复制整个目录文件
                AppGlobal.Log("开始复制替换升级文件");
                StringBuilder sbMessage = new StringBuilder();
                int countSuccess = 0;
                int countFail = 0;
                CopyFileCheckSelfExe(AppGlobal.TempDirectoryPath, AppGlobal.ExeDirectory, ref countSuccess, ref countFail, ref sbMessage);
                if (countFail > 0)
                {
                    AppGlobal.Log("复制文件,成功" + countSuccess + "个,失败" + countFail + "个," + sbMessage.ToString());
                }
                AppGlobal.Log("完成复制替换升级文件");

                //删除临时目录
                AppGlobal.Log("开始删除临时文件目录");
                Directory.Delete(AppGlobal.TempDirectoryPath, true);
                AppGlobal.Log("完成删除临时文件目录");

                copyOk = true;
            }
            catch (Exception ex)
            {
                string message = "复制替换文件出错:" + ex.Message;
                AppGlobal.Log(message);
                AppGlobal.LogDebug("Step_3," + message);

                //如果不是静默升级,则弹出消息框
                if (!isNoWindow)
                {
                    MessageBox.Show(message, AppGlobal.AppNameText, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }

            //TODO:3.替换xml配置文件


            //开始执行动作
            if (copyOk)
            {
                AppGlobal.Log("开始执行Action动作");
                AppUpdater.GetMe().DoActionUpdateItems();
                AppGlobal.Log("完成执行Action动作");
            }

            //更新自身tupdater.exe文件
            if (needUpdateSelfExe)
            {
                AppGlobal.Log("需要更新自身");
                ExitAndDeleteSelf(1, AppGlobal.ExeSelfUpdateFileName, AppGlobal.IniApplication);
                AppGlobal.Log("完成调用更新自身");
            }
            else
            {
                //运行主客户程序EXE
                if (!string.IsNullOrEmpty(AppGlobal.IniApplication))
                {
                    string startFile = string.Format("{0}\\{1}",
                        Directory.GetCurrentDirectory().TrimEnd('\\'),
                        AppGlobal.IniApplication
                        );
                    if (File.Exists(startFile))
                    {
                        Process.Start(startFile); //运行客户主程序EXE
                    }
                }
            }

            //完成
            AppGlobal.Log("完成,即将退出更新程序");
            return true;
        }

        /// <summary>
        /// 第三步,替换文件,执行Action动作
        /// </summary>
        /// <returns></returns>
        private bool Step_3(out string message)
        {
#if DEBUG
            //AppGlobal.Log("DEBUG模拟耗时");
            //Thread.Sleep(10*1000);
#endif

            message = null;

            //1.先复制xml配置文件
            AppGlobal.LogDebug("Step_3,替换文件,执行Action动作");
            AppGlobal.Log("开始复制替换XML配置文件");

            //先复制xml配置文件(防止出错中断进入死循环)
            //但若复制其它文件出错,将无法真正更新,例如dll被占用
            File.Copy(AppGlobal.XmlPathName_LocalFile, AppGlobal.XmlPathName_LocalFile_Bak, true);//先备份
            File.Copy(AppGlobal.XmlPathName_ServerFile, AppGlobal.XmlPathName_LocalFile, true);//更新xml配置文件

            AppGlobal.LogDebug("Step_3,完成复制替换XML配置文件,下面复制其它文件.");
            AppGlobal.Log("完成复制替换XML配置文件");

            //2.再复制其它文件
            bool copyOk = false;
            try
            {
                //复制整个目录文件
                AppGlobal.Log("开始复制替换升级文件");
                StringBuilder sbMessage = new StringBuilder();
                int countSuccess = 0;
                int countFail = 0;
                CopyFileCheckSelfExe(AppGlobal.TempDirectoryPath, AppGlobal.ExeDirectory, ref countSuccess, ref countFail, ref sbMessage);
                if (countFail <= 0)
                {
                    AppGlobal.Log("完成复制替换升级文件(无失败文件)");

                    //删除临时目录
                    AppGlobal.Log("开始删除临时文件目录");
                    Directory.Delete(AppGlobal.TempDirectoryPath, true);
                    AppGlobal.Log("完成删除临时文件目录");
                    copyOk = true;
                }
                else
                {
                    message = "复制文件未全部完成,成功" + countSuccess + "个,失败" + countFail + "个," + sbMessage.ToString();
                    AppGlobal.Log(message);
                    copyOk = false;

                    //如果不是静默升级,则弹出消息框
                    if (!isNoWindow)
                    {
                        MessageBox.Show(message, AppGlobal.AppNameText, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
            catch (Exception ex)
            {
                message = "复制替换文件出错:" + ex.Message;
                AppGlobal.Log(message);
                AppGlobal.LogDebug("Step_3," + message);

                //如果不是静默升级,则弹出消息框
                if (!isNoWindow)
                {
                    MessageBox.Show(message, AppGlobal.AppNameText, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }

            //3.若复制失败,还原xml配置文件,并退出exe,以便下一次全部更新
            if (!copyOk)
            {
                AppGlobal.Log("复制文件失败,还原配置文件");
                AppGlobal.LogDebug("Step_3,复制文件失败,还原配置文件");

                //还原备份
                File.Copy(AppGlobal.XmlPathName_LocalFile_Bak, AppGlobal.XmlPathName_LocalFile, true);
                message = string.Format("{0},{1}", message, "还原了更新配置文件,请下次重新更新或完全更新");
                return false;
            }

            //开始执行动作
            if (copyOk)
            {
                AppGlobal.Log("开始执行Action动作");
                AppUpdater.GetMe().DoActionUpdateItems();
                AppGlobal.Log("完成执行Action动作");
            }

            //更新自身tupdater.exe文件
            if (needUpdateSelfExe)
            {
                AppGlobal.Log("需要更新自身");
                ExitAndDeleteSelf(1, AppGlobal.ExeSelfUpdateFileName, AppGlobal.IniApplication);
                AppGlobal.Log("完成调用更新自身");
            }
            else
            {
                //运行主客户程序EXE
                if (!string.IsNullOrEmpty(AppGlobal.IniApplication))
                {
                    string startFile = string.Format("{0}\\{1}",
                        Directory.GetCurrentDirectory().TrimEnd('\\'),
                        AppGlobal.IniApplication
                        );
                    if (File.Exists(startFile))
                    {
                        Process.Start(startFile); //运行客户主程序EXE
                    }
                }
            }

            //8.删除备份配置文件
            if (File.Exists(AppGlobal.XmlPathName_LocalFile_Bak))
            {
                try
                {
                    File.Delete(AppGlobal.XmlPathName_LocalFile_Bak);
                }
                catch{}
            }

            //9.完成
            AppGlobal.Log("完成,即将退出更新程序");
            return true;
        }

        /// <summary>
        /// 2.下一步(开始下载)
        /// </summary>
        private void btnNext_Click(object sender, EventArgs e)
        {
            AppGlobal.LogDebug("btnNext_Click,Step_2前");
            Step_2();
            AppGlobal.LogDebug("btnNext_Click,Step_2后");
        }

        #region 后台下载

        private BackgroundWorker _bWorkerDownload;

        private int _downloadErrCount = 0;

        /// <summary>
        /// 初始化"后台下载器"
        /// </summary>
        private void InitializeBackgoundWorker()
        {
            _bWorkerDownload = new BackgroundWorker();
            _bWorkerDownload.WorkerReportsProgress = true;      //支持报告进度
            _bWorkerDownload.WorkerSupportsCancellation = true; //支持取消操作

            _bWorkerDownload.DoWork += new DoWorkEventHandler(BWorkerDownloadDoWork);
            _bWorkerDownload.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BWorkerDownloadRunWorkerCompleted);
            _bWorkerDownload.ProgressChanged += new ProgressChangedEventHandler(BWorkerDownloadProgressChanged);
        }

        /// <summary>
        /// "后台下载器"执行工作
        /// </summary>
        private void BWorkerDownloadDoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            _downloadErrCount = 0;
            DownUpdateFile(worker, e);
        }

        /// <summary>
        /// "后台下载器"工作已完成
        /// </summary>
        private void BWorkerDownloadRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            AppGlobal.Log("后台工作:完成(开始)");

            bool successCompleted = false;
            if (e.Error != null)
            {
                string msg = "下载完成，但发生错误：" + e.Error.Message;
                AppGlobal.Log(msg);

                //如果不是静默升级,则弹出消息框
                if (!isNoWindow)
                {
                    MessageBox.Show(msg, AppGlobal.AppNameText);
                }
            }
            else if (e.Cancelled)
            {
                lbState.Text = "被取消";
            }
            else
            {
                lbState.Text = "已完成";
                successCompleted = true;
            }

            //
            if (_downloadErrCount > 0)
            {
                string msg = "下载更新时产生 " + _downloadErrCount + " 个错误，请重新下载或联系管理员。";
                AppGlobal.Log(msg);

                //如果不是静默升级,则弹出消息框
                if (!isNoWindow)
                {
                    MessageBox.Show(msg, Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
            //else
            //{
            BtnNext.Enabled = true;
            BtnCancel.Enabled = false;

            InvalidateControl(); //重新绘制窗体部分控件属性
            //}

            AppGlobal.Log("后台工作:完成(结束)");

            //进入下一步
            if (successCompleted && _downloadErrCount == 0)
            {
                AppGlobal.Log("成功完成无错误,自动进入下一步");

                //2.是否自动进入下一步
                if (CbAutoNext.Checked)
                {
                    btnFinish_Click(sender, e);
                }
            }
            else if (!isNoWindow)
            {
                //静默升级
                AppGlobal.Log("静默升级,自动进入下一步");

                //2.是否自动进入下一步
                if (CbAutoNext.Checked)
                {
                    btnFinish_Click(sender, e);
                }
            }


        }

        /// <summary>
        /// "后台下载器"工作进度变化
        /// </summary>
        private void BWorkerDownloadProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //AppGlobal.Log("后台工作:进度报告");

            //进度报告 this.progressBar1.Value = e.ProgressPercentage;
            this.ProgressBarDownFile.Value = e.ProgressPercentage;

            if (e.UserState != null)
            {
                UpdateState updateState = e.UserState as UpdateState;
                if (updateState != null)
                {
                    lbState.Text = string.Format("正在下载第 {0} 个文件，共计 {1} 个文件。", updateState.CurrentFileIndex + 1, updateState.FileCount);
                    ListViewItem lvi = LvwUpdateNewList.Items[updateState.CurrentFileIndex];
                    if (lvi != null)
                    {
                        ListViewItem.ListViewSubItem lvsi = lvi.SubItems[2];//this.chProgress.Name
                        if (lvsi != null)
                        {
                            lvsi.Text = string.Format("{0} %", updateState.CurrentFileDownPercent);
                        }
                    }
                }
            }

            //InvalidateControl(); //重新绘制窗体部分控件属性(在下载进度变化时)
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        private void DownUpdateFile(BackgroundWorker worker, DoWorkEventArgs e)
        {
            AppGlobal.Log("关闭需更新但正在运行的EXE文件进程");

            //首先,关闭需更新却正在运行的EXE文件进程
            //mainAppExe = AppGlobal.IniApplication;
            Process[] allProcess = Process.GetProcesses();
            FileHelper.KillExeProcess(allProcess, AppGlobal.IniApplication);

            //取消
            if (worker.CancellationPending)
            {
                AppGlobal.Log("后台工作取消");
                e.Cancel = true;
                return;
            }

            //WebClient wcClient = new WebClient();
            //for (int i = 0; i < this.LvwUpdateNewList.Items.Count; i++)
            //for (int i = 0; i < appUpdater.UpdateItems.Count; i++)
            for (int i = 0; i < AppUpdater.GetMe().UpdateItems.Count; i++)
            {
                AppGlobal.Log("开始下载" + AppUpdater.GetMe().UpdateItems[i].Xml_Path);

                //服务端文件位置
                string serverFile = string.Format("{0}", AppUpdater.GetMe().UpdateItems[i].Xml_Path).Trim();
                if (serverFile.Length == 0)
                {
                    continue;//空的无需下载
                }
                string serverFileUrl = AppGlobal.IniUpdaterUrl + serverFile;

                //客户端文件保存位置
                string clientFile = string.Format("{0}", AppUpdater.GetMe().UpdateItems[i].Xml_Desname).Trim();
                if (clientFile.Length == 0)
                {
                    clientFile = serverFile;
                }

                //若是删除动作,则无需下载
                if (AppUpdater.GetMe().UpdateItems[i].IsActionDelete())
                {
                    AppGlobal.Log("跳过待删除文件" + AppUpdater.GetMe().UpdateItems[i].Xml_Path);
                    worker.ReportProgress(100, new UpdateState(AppUpdater.GetMe().UpdateItems.Count, i, 0));
                    continue;
                }


                //todo:待改为WebClient组件来下载,以免溢出
                //http://www.cnblogs.com/yulei/archive/2009/03/08/1406218.html
                //http://wguorun.blog.163.com/blog/static/16306238200810734553883/


                long fileLength = 0;

                WebRequest webReq = WebRequest.Create(serverFileUrl);
                WebResponse webRes = null;
                try
                {
                    webRes = webReq.GetResponse();
                }
                catch (WebException exResponse)
                {
                    //报告进度
                    //int percentFull = (int) ((i + 1)*10.00m/appUpdater.UpdateItems.Count*10.00m);
                    int percentFull = (int)((i + 1) * 10.00m / AppUpdater.GetMe().UpdateItems.Count * 10.00m);
                    //worker.ReportProgress(percentFull, new UpdateState(appUpdater.UpdateItems.Count, i, 0));
                    worker.ReportProgress(percentFull, new UpdateState(AppUpdater.GetMe().UpdateItems.Count, i, 0));

                    //记录错误
                    AppGlobal.Log("请求下载 " + serverFileUrl + " 出错");
                    AppGlobal.Log(exResponse);

                    _downloadErrCount += 1;
                    continue;
                }
                fileLength = webRes.ContentLength;

                //Debug.WriteLine("pbDownFile.Maximum = " + (int)fileLength);

                try
                {
                    Stream srm = webRes.GetResponseStream();
                    if (srm == null)
                    {
                        //报告进度
                        //int percentFull = (int) ((i + 1)*10.00m/appUpdater.UpdateItems.Count*10.00m);
                        int percentFull = (int)((i + 1) * 10.00m / AppUpdater.GetMe().UpdateItems.Count * 10.00m);
                        //worker.ReportProgress(percentFull, new UpdateState(appUpdater.UpdateItems.Count, i, 0));
                        worker.ReportProgress(percentFull, new UpdateState(AppUpdater.GetMe().UpdateItems.Count, i, 0));

                        //记录错误
                        AppGlobal.Log("下载 " + serverFileUrl + " 出错,Stream对象为空");
                        _downloadErrCount += 1;
                        continue;
                    }

                    #region 下载一个文件的进度

                    StreamReader srmReader = new StreamReader(srm);
                    byte[] bufferbyte = new byte[fileLength];
                    int allByte = (int) bufferbyte.Length;
                    int startByte = 0;
                    while (fileLength > 0)
                    {
                        Application.DoEvents();
                        int downByte = srm.Read(bufferbyte, startByte, allByte);
                            //BUG:IOException,无法从传输连接中读取数据,由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作。
                        if (downByte == 0)
                        {
                            break;
                        }
                        startByte += downByte;
                        allByte -= downByte;

                        //模拟耗时
                        //Thread.Sleep(0);

                        float part = (float) startByte/1024;
                        float total = (float) bufferbyte.Length/1024;
                        int percent = Convert.ToInt32((part/total)*100);

                        //进度报告
                        //int percentFull = (int) ((i + 1)*10.00m/appUpdater.UpdateItems.Count*10.00m);
                        int percentFull = (int) ((i + 1)*10.00m/AppUpdater.GetMe().UpdateItems.Count*10.00m);
                        //worker.ReportProgress(percentFull, new UpdateState(appUpdater.UpdateItems.Count, i, percent));
                        worker.ReportProgress(percentFull,
                            new UpdateState(AppUpdater.GetMe().UpdateItems.Count, i, percent));
                    } //while

                    #endregion

                    //string tempPath = AppGlobal.TempDirectoryPath.Trim('\\', '/') + "\\" + serverFile.Trim('\\', '/');
                    string tempPath = AppGlobal.TempDirectoryPath.Trim('\\', '/') + "\\" + clientFile.Trim('\\', '/');
                    FileHelper.CreateDirtory(tempPath);
                    FileStream fs = new FileStream(tempPath, FileMode.OpenOrCreate, FileAccess.Write);
                    fs.Write(bufferbyte, 0, bufferbyte.Length);
                    srm.Close();
                    srmReader.Close();
                    fs.Close();

                    //模拟耗时
                    Thread.Sleep(0);
                }
                catch (WebException ex)
                {
                    //报告进度
                    //int percentFull = (int) ((i + 1)*10.00m/appUpdater.UpdateItems.Count*10.00m);
                    int percentFull = (int)((i + 1) * 10.00m / AppUpdater.GetMe().UpdateItems.Count * 10.00m);
                    //worker.ReportProgress(percentFull, new UpdateState(appUpdater.UpdateItems.Count, i, 0));
                    worker.ReportProgress(percentFull, new UpdateState(AppUpdater.GetMe().UpdateItems.Count, i, 0));

                    //记录错误
                    AppGlobal.Log("请求下载 " + serverFileUrl + " 出错");
                    AppGlobal.Log(ex);

                    _downloadErrCount += 1;
                    continue;
                } //cache
            } //for
            //InvalidateControl();//线程间操作无效: 从不是创建控件 xxx 的线程访问它

            //this.Cursor = Cursors.Default;
        }

        #endregion

        /// <summary>
        /// 2.取消
        /// </summary>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            Step_2_Cancel();
        }

        /// <summary>
        /// 帮助
        /// </summary>
        private void BtnHelp_Click(object sender, EventArgs e)
        {
            new WinHelp.WizardFrm().ShowDialog();
        }

        #region 完成

        /// <summary>
        /// 3.完成
        /// </summary>
        private void btnFinish_Click(object sender, EventArgs e)
        {
            string message;
            bool ok = Step_3(out message);
            if (ok)
            {
                Close();
                Dispose();
            }
            else
            {
                //第三步失败
                //如果不是静默升级,则弹出消息框
                if (!isNoWindow)
                {
                    DialogResult drt = MessageBox.Show("更新失败，退出吗？", AppGlobal.AppNameText, MessageBoxButtons.OKCancel,
                        MessageBoxIcon.Asterisk);
                    if (drt == DialogResult.OK)
                    {
                        Close();
                        Dispose();
                    }
                    else
                    {
                        //可以关闭文件占用,再点一次按钮.
                    }
                }
                else
                {
                    //静默
                    ShowBalloon("更新失败(step3):" + message, ToolTipIcon.Error);
                    Close();
                    Dispose();
                }
            }
        }

        /// <summary>
        /// 是否需要自身升级
        /// </summary>
        private bool needUpdateSelfExe = false;

        /// <summary>
        /// 递归复制文件(自身EXE则重命名)
        /// </summary>
        private void CopyFileCheckSelfExe_2(string sourcePath, string objPath, ref int countSuccess,
            ref int countFail, ref StringBuilder sbMessage)
        {
            //char[] split = @"\".ToCharArray();
            if (!Directory.Exists(objPath))
            {
                DirectoryInfo dif = Directory.CreateDirectory(objPath);
            }
            string[] files = Directory.GetFiles(sourcePath);
            for (int i = 0; i < files.Length; i++)
            {
                string[] childfile = files[i].Split('\\');
                string source = files[i];
                string target = objPath + @"\" + childfile[childfile.Length - 1];
                try
                {
                    //File.Copy(files[i], objPath + @"\" + childfile[childfile.Length - 1], true);
                    bool isSelf = CheckSelfUpdateExe(ref target);
                    if (isSelf)
                    {
                        needUpdateSelfExe = true; //需要更新自身
                    }
                    
                    //复制文件
                    //File.Copy(source, target, true);
                    if (File.Exists(target))
                    {
                        //文件存在
                        if (FileHelper.IsFileInUse(target))
                        {
                            //文件正在被使用
                            sbMessage.AppendLine("文件正被使用无法复制替换:" + target);
                            countFail += 1; //失败
                            continue;//继续下一轮
                        }
                    }

                    countSuccess += 1; //成功
                }
                catch (Exception ex)
                {
                    sbMessage.AppendLine("复制文件出错:" + source + "," + ex.Message);
                    countFail += 1; //失败
                }
            }

            //子目录
            string[] dirs = Directory.GetDirectories(sourcePath);
            for (int i = 0; i < dirs.Length; i++)
            {
                string[] childdir = dirs[i].Split('\\');
                CopyFileCheckSelfExe_2(dirs[i], objPath + @"\" + childdir[childdir.Length - 1], ref countSuccess, ref countFail, ref sbMessage); //递归调用
            }

            //
        }

        /// <summary>
        /// 递归复制文件(自身EXE则重命名)
        /// </summary>
        private void CopyFileCheckSelfExe(string sourcePath, string objPath, ref int countSuccess,
            ref int countFail, ref StringBuilder sbMessage)
        {
            //char[] split = @"\".ToCharArray();
            if (!Directory.Exists(objPath))
            {
                DirectoryInfo dif = Directory.CreateDirectory(objPath);
            }
            string[] files = Directory.GetFiles(sourcePath);
            for (int i = 0; i < files.Length; i++)
            {
                string[] childfile = files[i].Split('\\');
                try
                {
                    //File.Copy(files[i], objPath + @"\" + childfile[childfile.Length - 1], true);
                    string source = files[i];
                    string target = objPath + @"\" + childfile[childfile.Length - 1];
                    bool isSelf = CheckSelfUpdateExe(ref target);
                    if (isSelf)
                    {
                        needUpdateSelfExe = true; //需要更新自身
                    }
                    File.Copy(source, target, true);

                    countSuccess += 1; //成功
                }
                catch (Exception ex)
                {
                    sbMessage.AppendLine("复制文件出错:" + files[i] + "," + ex.Message);
                    countFail += 1; //失败
                }
            }

            //子目录
            string[] dirs = Directory.GetDirectories(sourcePath);
            for (int i = 0; i < dirs.Length; i++)
            {
                string[] childdir = dirs[i].Split('\\');
                CopyFileCheckSelfExe(dirs[i], objPath + @"\" + childdir[childdir.Length - 1], ref countSuccess,
                    ref countFail, ref sbMessage); //递归调用
            }

            //
        }
        #endregion

        #region 自身升级

        /// <summary>
        /// 更新自身检查
        /// </summary>
        /// <param name="objPathFileName"></param>
        private static bool CheckSelfUpdateExe(ref string objPathFileName)
        {
            objPathFileName = string.Format("{0}", objPathFileName).Trim();
            string selfExePathFileName = Application.ExecutablePath;
            bool isOk = String.Compare(objPathFileName, selfExePathFileName, StringComparison.InvariantCultureIgnoreCase) == 0;
            if (!isOk)
            {
                return false;
            }

            //更新文件名
            objPathFileName = AppGlobal.ExeSelfUpdateFileName;
            return true;
        }

        /// <summary>
        /// 退出自身并调用CMD命令
        /// </summary>
        /// <param name="delaySecond">延时秒数</param>
        /// <param name="newExe">新exe名称</param>
        /// <param name="startExe">启动exe文件名</param>
        private static void ExitAndDeleteSelf(int delaySecond,string newExe,string startExe)
        {
            //延时秒数
            delaySecond = Math.Abs(delaySecond);

            //启动exe文件名
            newExe = string.Format("{0}", newExe).Trim();

            //启动exe文件名
            startExe = string.Format("{0}", startExe).Trim();

            //my exe name(old)
            int pos = Application.ExecutablePath.LastIndexOf('\\') + 1;
            string oldExe = Application.ExecutablePath.Substring(pos);

            //命令行(延时删除)
            string cmdArgs = string.Format("/C choice /t {0} /d y /n > nul & del {1}", delaySecond, oldExe);
            //命令行(重命名)
            if (newExe.Length > 2)
            {
                cmdArgs = string.Format("{0} & rename {1} {2}", cmdArgs, newExe, oldExe);
            }
            //命令行(启动程序)
            if (startExe.Length > 2)
            {
                cmdArgs = string.Format("{0} & start {1}", cmdArgs, startExe);
            }

            //运行cmd
            ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", cmdArgs);
            psi.WindowStyle = ProcessWindowStyle.Hidden;
            psi.CreateNoWindow = true;
            Process.Start(psi); //运行延时cmd删除并重命名替换tupdater自己
            Application.Exit();
        }

        #endregion

        /// <summary>
        /// 重新绘制窗体部分控件属性
        /// </summary>
        private void InvalidateControl()
        {
            PanelRightStep2.Location = PanelRightStep1.Location;
            PanelRightStep2.Size = PanelRightStep1.Size;
            PanelRightStep1.Visible = false;
            PanelRightStep2.Visible = true;

            BtnNext.Visible = false;
            BtnCancel.Visible = false;
            BtnFinish.Location = BtnCancel.Location;
            BtnFinish.Visible = true;
        }

    }
}
